import tkinter as tk
from tkinter import ttk, scrolledtext, messagebox
from bilibili_danmu_analysis.danmu_crawler.spider import CrawlManager
from bilibili_danmu_analysis.utils import Utils


class CrawlFrame(ttk.Frame):
    def __init__(self, parent, data_store):
        super().__init__(parent)
        self.parent = parent
        self.utils= Utils()
        self.data_store = data_store
        self._setup_ui()
        self._setup_data_handling()
        parent.protocol("WM_DELETE_WINDOW", self._safe_exit)

    def _safe_exit(self):
        """安全退出处理"""
        self._stop_crawl()
        if self.crawl_manager and self.crawl_manager.crawl_thread.is_alive():
            self.crawl_manager.crawl_thread.join(timeout=2)
        self.parent.destroy()

    def _setup_ui(self):
        """界面布局"""
        # 输入控制区域
        control_frame = ttk.Frame(self)
        control_frame.pack(pady=10, fill=tk.X, padx=5)

        self.bvid_entry = ttk.Entry(control_frame, width=20)
        self.bvid_entry.pack(side=tk.LEFT, padx=5, fill=tk.X, expand=True)
        self.bvid_entry.bind("<Key>", self._on_key_pressed)  # 键盘输入限制
        self.bvid_entry.bind("<<Paste>>", self._on_paste)  # 粘贴处理
        self.bvid_entry.bind("<FocusIn>", self._on_focus_in)  # 焦点事件
        self.bvid_entry.bind("<Return>", lambda e: self._toggle_crawl())

        self.start_btn = ttk.Button(
            control_frame,
            text="▶ 开始采集",
            command=self._toggle_crawl,
            width=10
        )
        self.start_btn.pack(side=tk.LEFT, padx=5)

        self.clear_btn = ttk.Button(
            control_frame,
            text="🗑 清空输入",
            command=self._clear_data,
        )
        self.clear_btn.pack(side=tk.LEFT, padx=5)

        # 日志显示区域
        log_frame = ttk.LabelFrame(self, text="采集日志")
        log_frame.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)

        self.log_area = scrolledtext.ScrolledText(
            log_frame,
            wrap=tk.WORD,
            state='disabled',
            font=('Consolas', 9),
            height=10
        )
        self.log_area.pack(fill=tk.BOTH, expand=True)

    def _on_key_pressed(self, event):
        """输入限制"""
        if event.char not in "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ":
            self._log_message("输入错误，只能输入数字、字母和下划线", level="ERROR")
            return "break"
        if len(self.bvid_entry.get()) >= 12:
            messagebox.showinfo("输入错误", "BVID长度不能超过12位", parent=self)
            return "break"


    def _on_paste(self, event):
        """粘贴处理"""
        try:
            # 获取剪贴板内容并处理
            clipboard_text = self.parent.clipboard_get().strip()
            message= self.utils.validate_bvid(clipboard_text)
            if message.get("code")==200:
                valid_bvid = clipboard_text
                self.bvid_entry.delete(0, tk.END)
                self.bvid_entry.insert(0, valid_bvid)
            else:
                messagebox.showerror("输入错误", message.get("message"), parent=self)
            # 阻止默认粘贴行为
            return "break"
        except Exception as e:
            self._log_message(f"粘贴处理失败: {str(e)}", level="ERROR")

    def _on_focus_in(self, event):
        self.bvid_entry.select_range(0, tk.END)
        self.bvid_entry.icursor(tk.END)
        return "break"

    def _setup_data_handling(self):
        """初始化数据管理组件"""
        self.crawl_manager = None
        self.update_interval = 200  # 界面更新间隔(ms)

    def _toggle_crawl(self):
        """切换采集状态"""
        if self.start_btn["text"].startswith("▶"):
            bvid = self.bvid_entry.get().strip()
            message = self.utils.validate_bvid(bvid)
            if message.get("code") != 200:
                messagebox.showerror("输入错误", message.get("message"), parent=self)
                return
            self._start_crawl(bvid)
        else:
            self._stop_crawl()

    def _start_crawl(self,bvid):
        """启动采集流程"""
        if self.crawl_manager and self.crawl_manager.crawl_thread.is_alive():
            self._log_message("已有任务在运行，请先停止当前任务", level="ERROR")
            return
        # 初始化爬虫管理器
        self.crawl_manager = CrawlManager()
        self.crawl_manager.start_crawl(bvid)

        # 更新界面状态
        self.bvid_entry.config(state=tk.DISABLED)
        self.clear_btn.config(state=tk.DISABLED)
        self.start_btn.config(text="⏹ 停止采集")
        self._log_clear()
        self._log_message(f"初始化采集任务: {bvid}")

        # 启动监控循环
        self._schedule_update()

    def _clear_data(self):
        """清空数据"""
        self.bvid_entry.delete(0, tk.END)

    def _schedule_update(self):
        """定时更新界面状态"""
        if self.crawl_manager and not self.crawl_manager.stop_event.is_set():
            self._process_updates()
            self.parent.after(self.update_interval, self._schedule_update)

    def _process_updates(self):
        """使用异步方式处理更新"""
        try:
            if self.crawl_manager is None:
                self._log_message("crawl_manager is None", level="ERROR")
                return
            updates = self.crawl_manager.get_updates()
            for update in updates:
                msg_type = update.get('type')
                if msg_type == 'data':
                    self._handle_data_update(update)
                elif msg_type == 'error':
                    self._handle_error(update)
                elif msg_type == 'finish':
                    self._handle_completion(update)
                elif msg_type == 'log':
                    self._log_message(update.get('msg'))

            # 检查线程状态
            if not self.crawl_manager.crawl_thread.is_alive():
                self._handle_completion({'type': 'finish', 'total': len(self.data_store)})
        except Exception as e:
            self._log_message(f"更新处理异常: {str(e)}", level="ERROR")

    def _handle_data_update(self, update):
        """处理数据更新"""
        new_data = update.get('data', [])
        count = update.get('count', 0)

        self.data_store.extend(new_data)
        self._log_message(f"收到 {count} 条新数据")

    def _handle_error(self, update):
        """处理错误信息"""
        error_msg = update.get('msg', '未知错误')
        self._log_message(f"错误: {error_msg}", level="ERROR")
        self._stop_crawl()

    def _handle_completion(self, update):
        """处理完成事件"""
        self._log_message(f"采集完成", level="SUCCESS")
        messagebox.showinfo("采集完成", "采集成功", parent=self)
        self._stop_crawl()

    def _stop_crawl(self):
        """停止采集任务"""
        if self.crawl_manager:
            try:
                self.crawl_manager.stop_crawl()
                self._log_message("停止采集任务")
                messagebox.showinfo("info", "采集任务已停止", parent=self)
            except Exception as e:
                self._log_message(f"停止采集任务失败: {str(e)}", level="ERROR")
            finally:
                self.crawl_manager = None

        self.bvid_entry.config(state=tk.NORMAL)
        self.clear_btn.config(state=tk.NORMAL)
        self.start_btn.config(text="▶ 开始采集")

    def _log_message(self, message, level="INFO"):
        """分级带自动滚动和行数限制的日志记录"""
        tag_config = {
            "INFO": {"foreground": "black"},
            "ERROR": {"foreground": "red", "font": ('微软雅黑', 9, 'bold')},
            "SUCCESS": {"foreground": "green", "font": ('微软雅黑', 9, 'bold')}
        }
        max_lines = 500  # 最大保留行数

        self.log_area.config(state=tk.NORMAL)

        # 插入新日志
        self.log_area.insert(tk.END, f"[{level}] {message}\n", (level,))
        for tag, config in tag_config.items():
            self.log_area.tag_configure(tag, **config)

        # 限制日志行数
        current_lines = int(self.log_area.index('end-1c').split('.')[0])
        if current_lines > max_lines:
            delete_count = current_lines - max_lines
            self.log_area.delete(1.0, f"{delete_count}.0")

        # 自动滚动到底部
        self.log_area.see(tk.END)
        self.log_area.config(state=tk.DISABLED)

    def _log_clear(self):
        """清空日志"""
        self.log_area.config(state=tk.NORMAL)
        self.log_area.delete(1.0, tk.END)
        self.log_area.config(state=tk.DISABLED)


if __name__ == "__main__":
    root = tk.Tk()
    root.title("B站弹幕采集器 v1.2")
    root.geometry("600x400")

    frame = CrawlFrame(root, data_store=[])
    frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)

    root.mainloop()
